#pragma once

#include <iostream>
#include <vector>
#include <functional>
#include <atomic>
#include <unordered_map>
#include <algorithm>
#include <fstream>
#include <sstream>

#include <boost/algorithm/string.hpp>
#include <mysql/mysql.h>

#include <cassert>

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <signal.h>

#include <jsoncpp/json/json.h>

namespace Ns_Comm
{
    enum
    {
        CodeEmptyErr = -6,
        UnknownErr = -5,
        ComplieErr = -4,
        OpenFile_Err,
        WaitChild_Err,
        Fork_Err,
        Success
    };

    class TimeTool
    {
    public:
        static std::string MillisecondLevelTime()
        {
            struct timeval tv;
            gettimeofday(&tv, nullptr);

            return std::to_string(tv.tv_sec * 1000 + tv.tv_usec / 1000);
        }
    };

    const std::string temporary_path = "./temp/";
    class PathTool
    {
    private:
        static std::string SplicingStrings(const std::string &filename, const std::string &suffix)
        {
            return temporary_path + filename + suffix;
        }

    public:
        // 编译时拼接临时文件
        static std::string SplicingCpp(const std::string &filename)
        {
            return temporary_path + filename + ".cpp";
        }
        static std::string SplicingExe(const std::string &filename)
        {
            return temporary_path + filename + ".exe";
        }
        static std::string SplicingCompilerErr(const std::string &filename)
        {
            return temporary_path + filename + ".compile_err";
        }

        // 运行时拼接临时文件
        static std::string SplicingExeStdin(const std::string &filename)
        {
            return temporary_path + filename + ".stdin";
        }
        static std::string SplicingExeStdout(const std::string &filename)
        {
            return temporary_path + filename + ".stdout";
        }
        static std::string SplicingExeStderr(const std::string &filename)
        {
            return temporary_path + filename + ".stderr";
        }

        // 拼接题库数据文件
        // 拼接全部题目列表的文件路径
        static std::string SplicingQuestionsList(const std::string &filename)
        {
            return filename + "question.list";
        }
        // 拼接对应题目的题目描述路径
        static std::string SplicingQuestionDesc(const std::string &number, const std::string &filename)
        {
            return filename + number + "/desc.txt";
        }
        // 拼接用户代码块的代码文件路径
        static std::string SplicingQuestionHeader(const std::string &number, const std::string &filename)
        {
            return filename + number + "/header.cpp";
        }
        // 拼接测试代码的用例的文件路径
        static std::string SplicingQuestionTail(const std::string &number, const std::string &filename)
        {
            return filename + number + "/tail.cpp";
        }
    };

    class FileTool
    {
    public:
        static void Redirect(int src, int dest)
        {
            // fd  1
            dup2(dest, src);
        }

        static int Open(const std::string &filename, int flag, int mode)
        {
            int fd = open(filename.c_str(), flag, mode);
            return fd;
        }
        static int Open(const std::string &filename, int flag)
        {
            int fd = open(filename.c_str(), flag);
            return fd;
        }

        static bool FileWrite(int fd, const std::string content)
        {
            ssize_t n = write(fd, content.c_str(), content.size());
            return n;
        }

        static bool FileWrite(const std::string &filename, const std::string content)
        {
            std::ofstream out(filename);
            if (!out.is_open())
                return false;

            out << content;

            return true;
        }
        static std::string FileRead(int fd)
        {
            char buffer[4096];
            ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
            buffer[n] = 0;
            std::string ret = buffer;
            return ret;
        }

        static bool FileRead(const std::string filename, std::string *out, bool keep = false)
        {
            std::ifstream in(filename);
            if (!in.is_open())
                return false;

            std::string line;
            while (getline(in, line))
            {
                *out += line;
                *out += keep ? "\n" : "";
            }
            return true;
        }

        static bool IsExist(const std::string &filename)
        {
            struct stat st;
            int n = stat(filename.c_str(), &st);
            if (n == 0)
                return true;
            else
                return false;
        }

        static std::string UniqueFileName()
        {
            std::string ms = TimeTool::MillisecondLevelTime();
            static std::atomic_uint id(0);
            std::string strid = std::to_string(id);

            return ms + strid;
        }

        static void Close(int fd)
        {
            close(fd);
        }
    };

    class StringTool
    {
    public:
        static bool Spilit(const std::string &line, std::vector<std::string> *v, const std::string &sep)
        {
            // boost库 分割字符串
            boost::split(*v, line, boost::is_any_of(sep), boost::token_compress_on);
            return true;
        }
    };

    class MySQLHelpper
    {

    public:
        MySQLHelpper()
        {
            _handler = mysql_init(nullptr);
        }

        bool connect(const std::string& host, const std::string& user, const std::string passwd, const std::string& dbname, int port)
        {
            if (mysql_real_connect(_handler, host.c_str(), user.c_str(), passwd.c_str(), dbname.c_str(), port, nullptr, 0) == nullptr)
            {
                std::cout << "连接数据库错误，错误原因：" << mysql_error(_handler) << std::endl;
                close();
                return false;
            }

            return true;
        }

        template <typename Func>
        bool excuteSQL(const std::string &sql, Func &&exec)
        {
            if (mysql_query(_handler, sql.c_str()) != 0)
            {
                std::cout << "执行sql语句" << sql << "失败，" << "失败原因：" << mysql_error(_handler) << std::endl;
                return false;
            }

            MYSQL_RES *result = mysql_store_result(_handler);
            if (result == nullptr)
            {
                std::cout << "查询语句获取结果集失败，失败原因：" << mysql_error(_handler) << std::endl;
                return false;
            }

            int num_fields = mysql_num_fields(result);
            MYSQL_ROW row;
            while ((row = mysql_fetch_row(result)))
            {
                std::vector<std::string> row_v;
                for (int i = 0; i < num_fields; i++)
                {
                    if (row[i] != nullptr)
                        row_v.push_back(row[i]);
                    else
                        row_v.push_back("");
                }
                std::forward<Func>(exec)(row_v);
            }
            mysql_free_result(result);
            return true;
        }

        bool excuteSQL(const std::string &sql)
        {
            if (mysql_query(_handler, sql.c_str()) != 0)
            {
                std::cout << "执行sql语句" << sql << "失败，" << "失败原因：" << mysql_error(_handler) << std::endl;
                // mysql_close(_handler);
                return false;
            }
            return true;
        }

        void close()
        {
            mysql_close(_handler);
        }

        ~MySQLHelpper()
        {
            close();
        }

    private:
        MYSQL *_handler;
    };
}